隔离级别有哪些?

未提交读

事务还没有提交的修改,其他事务都可以读取到。可能会有脏读的问题,就是读到一些未提交的脏数据。

提交读

其他事务提交的修改,事务在执行过程中可以读取到,如果一个事务在执行过程中需要两次读取同一行数据,可能会不一致。一般发生在UPDATE和DELETE操作。(大部分数据库系统是采用的这个,但是mysql不是)

这个隔离级别下,读是不加锁的,写,更新,删除是加锁的,如果更新的行是可以通过索引查找到,那么是对这些行加行锁,否则会将所有行都加锁,然后返回给MySQL Server,让他来进行过滤,对于不满足条件的行解锁。

但是还是会有幻读的问题发生(幻读就是事务A在读取和写入符合的条件的记录时,其他事务又插入了一条符合条件的记录,此时事务A二次读取时会产生幻行,一般发生在INSERT操作。)

可重复读

在事务开始时,记录当时的状态,在第二次读取同一行数据时,除非是本事务做的修改,否则读取的都是事务开始时的数据。可以解决脏读的问题,没法解决幻读的问题。这是MySQL的默认事务隔离级别。(MySQL在可重复读的隔离级别下,通过MVCC机制和Next-key Lock解决了幻读的问题。)

可串行化

强制事务串行执行,会让读取每一行都加锁,读用读锁,写用写锁,读写锁互斥,可以解决幻读的问题。并发比较多的话可能会造成大量的超时等待和锁竞争。如果业务并发的特别少或者没有并发,同时又要求数据及时可靠的话

MVCC的实现原理

mvcc主要适用于可重复读,可以解决幻读的问题。

innodb在解决幻读的问题主要是通MVVC 多版本并发版本控制来实现的

就是每一行数据中额外保存两个隐藏的列:

插入或上次更新该行的事务ID(删除也被认为是一次更新,只不过在代表删除的更新操作中,行中的特殊位被设置为将其标记为已删除。这个事务ID可以认为是数据行的修改版本号。)

滚动指针(指向undo log中用于事务回滚的日志记录)。

具体流程:

1.插入操作

每次开始事务时,会对系统版本号+1作为当前事务的版本号。

插入数据后,将事务的版本号作为数据行的创建版本号。

2.删除操作

在使用SQL语句删除行时,并不会立即将其从数据库中物理删除,只会将其标记为删除,并且修改更新该行的事务ID。(InnoDB只会在丢弃为删除而编写的undo log日志记录时,才物理删除相应的行及其索引记录。此删除操作称为purge,它非常快,通常花费与执行删除操作的SQL语句相同的时间顺序。)

4.或更新操作

将当前的事务版本号作为数据行的更新版本号。

5.查询操作

数据行要被查询出来必须满足两个条件,

数据行没有标记为删除或者标记为删除但是删除的事务ID>当前事务ID的数据(否则数据已经被标记删除了)

更新事务ID<=当前事务ID的数据(否则数据是后面的事务创建出来的,或者是被修改过的,那么需要去undo log中找上次的快照数据。)

如果查询时,该行数据被加了X锁,那么读数据的事务不会进行等待,而是会根据该行数据中的回滚指针undo log日志中读之前版本的数据(这里存储的数据本身是用于回滚的),在可重复读的隔离级别下,从undo log中读取的数据总是事务开始时的快照数据(也就是版本号小于当前事务id的数据),在提交读的隔离级别下,从undo log中读取的总是最新的快照数据。